package com.dubious.interview.graph;

import java.lang.StringBuilder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class GraphByList<V extends Object, E extends Object> {

    private List<V> vertices;
    private List<List<Edge<E>>> edges;
    
    public GraphByList(int numVertices)
    {
        vertices = Arrays.asList((V[])new Object[numVertices]);
        edges = new ArrayList<List<Edge<E>>>(numVertices);
        for(int i = 0; i < numVertices; i++)
        {
            edges.add(new LinkedList<Edge<E>>());
        }
    }
    
    public GraphByList<V,E> withVertexValue(int vertex, V value)
    {
        if(vertex > vertices.size())
        {
            throw new RuntimeException("Vertex index out of bounds: " + vertex);
        }
        
        vertices.set(vertex, value);
        
        return this;
    }
    
    public GraphByList<V,E> withEdge(int vertexA, int vertexB, E weight)
    {
        if(vertexA > edges.size())
        {
            throw new RuntimeException("VertexA index out of bounds: " + vertexA);
        } else if(vertexB > edges.size())
        {
            throw new RuntimeException("VertexB index out of bounds: " + vertexB);
        }
        
        // for now assume that an edge will never be added twice
        edges.get(vertexA).add(new Edge<E>(vertexA, vertexB, weight));
        
        return this;
    }
    
    List<List<Edge<E>>> getEdges()
    {
        return edges;
    }
    
    @Override
    public boolean equals(Object otherObject)
    {
        if(otherObject == null || !(otherObject instanceof GraphByList))
        {
            return false;
        }
        
        GraphByList other = (GraphByList) otherObject;
        
        // compare vertices
        if(!other.vertices.equals(this.vertices))
        {
            return false;
        }
        
        // compare edges.  because we are going to disregard order in
        // the sublists of edges, we hand roll the comparison
        if(other.edges.size() != this.edges.size())
        {
            return false;
        }
        
        for(int i = 0; i < other.edges.size(); i++)
        {
            List<Edge> otherEdges = (List<Edge>)other.edges.get(i);
            List<Edge<E>> thisEdges = this.edges.get(i);
            if(otherEdges.size() != thisEdges.size())
            {
                return false;
            }
            
            for(Edge edge : otherEdges)
            {
                // we disregard order of the edges in the array
                if(!thisEdges.contains(edge))
                {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    @Override
    public int hashCode()
    {
        throw new UnsupportedOperationException("Not Yet Implemented");
    }
    
    @Override
    public String toString()
    {
        StringBuilder output = new StringBuilder();
        output.append("V=[");
        for(int i = 0; i < vertices.size(); i++)
        {
            if(vertices.get(i) != null)
            {
                output.append("(" + i + "=" + vertices.get(i).toString() + "),");
            }
        }
        output.append("],E=[");
        for(int i = 0; i < edges.size(); i++)
        {
            for(int j = 0; j < edges.get(i).size(); j++)
            {
                Edge<E> edge = edges.get(i).get(j);
                output.append(
                        "(" + i
                        + "," + edge.getVertexB()
                        + "=" + edge.getWeight()
                        + "),");
            }
        }
        output.append("]");
        
        return output.toString();
    }
}
